﻿using Styx;
using Styx.Pathing;
using Styx.WoWInternals;

namespace TheFinalCall
{
    internal static class MovementManager
    {
        private static INavigationProvider _prevNavigation = null;
        private static IPlayerMover _prevPlayerMover;
        private static IStuckHandler _prevStuckHandler;

        #region Initialization

        internal static void Init()
        {
            TheFinalCallRoutine.OnBotEvent += (src, arg) =>
                {
                    IsManualMovementBotActive = TheFinalCallRoutine.IsBotInUse("LazyRaider", "Raid Bot");
                    if (arg.Event == TheFinalCallBotEvent.BotStart)
                        Start();
                    else if (arg.Event == TheFinalCallBotEvent.BotStop)
                        Stop();
                    else if (arg.Event == TheFinalCallBotEvent.BotChanged)
                        Change();
                };
        }

        /// <summary>
        ///     Update the current status of MovementManager.  Should be called when an
        ///     outside influence has possibly caused a configuration change, such as
        ///     settings window, user interface bot change, etc.
        /// </summary>
        public static void Update()
        {
            if (IsBotMovementDisabled)
                SuppressMovement();
            else
                AllowMovement();
        }

        #endregion

        #region Event Handlers

        private static void Start()
        {
            Update();
        }

        private static void Stop()
        {
            // restore in case we had taken over 
            AllowMovement();
        }

        private static void Change()
        {
            // restore in case we had taken over
            AllowMovement();
        }

        #endregion

        #region Movement Handler Primitives

        private static void AllowMovement()
        {
            if (Navigator.PlayerMover == pNoPlayerMovement)
            {
                Logger.WriteDebug("MovementManager: restoring Player Movement");
                Navigator.PlayerMover = _prevPlayerMover;
            }

            //if (Navigator.NavigationProvider == pNoNavigation)
            //{
            //    Logger.WriteDebug("MovementManager: restoring Player Navigation");
            //    Navigator.NavigationProvider = _prevNavigation;
            //}

            if (Navigator.NavigationProvider.StuckHandler == pNoStuckHandling)
            {
                Logger.WriteDebug("MovementManager: restoring Stuck Handler");
                Navigator.NavigationProvider.StuckHandler = _prevStuckHandler;
            }
        }

        private static void SuppressMovement()
        {
            if (Navigator.PlayerMover != pNoPlayerMovement)
            {
                Logger.WriteDebug("MovementManager: setting No Player Movement");
                _prevPlayerMover = Navigator.PlayerMover;
                Navigator.PlayerMover = pNoPlayerMovement;
            }

            //if (Navigator.NavigationProvider != pNoNavigation)
            //{
            //    Logger.WriteDebug("MovementManager: setting No Player Navigation");
            //    _prevNavigation = Navigator.NavigationProvider;
            //    Navigator.NavigationProvider = pNoNavigation;
            //}

            if (Navigator.NavigationProvider.StuckHandler != pNoStuckHandling)
            {
                Logger.WriteDebug("MovementManager: setting No Stuck Handling");
                _prevStuckHandler = Navigator.NavigationProvider.StuckHandler;
                Navigator.NavigationProvider.StuckHandler = pNoStuckHandling;
            }
        }

        #endregion

        #region Local Classes for No Movement Providers

        private static NoNavigation pNoNavigation = new NoNavigation();
        private static readonly NoPlayerMovement pNoPlayerMovement = new NoPlayerMovement();
        private static readonly NoStuckHandling pNoStuckHandling = new NoStuckHandling();

        private class NoNavigation : INavigationProvider
        {
            public NoNavigation()
            {
                StuckHandler = new NoStuckHandling();
            }

            public bool CanNavigateFully(WoWPoint from, WoWPoint to, int maxHops)
            {
                return true;
            }

            public bool Clear()
            {
                return true;
            }

            public WoWPoint[] GeneratePath(WoWPoint from, WoWPoint to)
            {
                return new[] {new WoWPoint(from.X, from.Y, from.Z)};
            }

            public MoveResult MoveTo(WoWPoint location)
            {
                return MoveResult.Moved;
            }

            public float PathPrecision { get; set; }
            public IStuckHandler StuckHandler { get; set; }
        }

        private class NoPlayerMovement : IPlayerMover
        {
            public void Move(WoWMovement.MovementDirection direction)
            {
            }

            public void MoveStop()
            {
            }

            public void MoveTowards(WoWPoint location)
            {
            }
        }

        private class NoStuckHandling : IStuckHandler
        {
            public bool IsStuck()
            {
                return false;
            }

            public void Reset()
            {
            }

            public void Unstick()
            {
            }
        }

        #endregion

        /// <summary>
        ///     True: TheFinalCall movement is currently disabled.  This could be due to a setting,
        ///     the current Bot, or a Hotkey toggled.  All code needing to check if
        ///     movement is allowed should call this or MovementManager.IsMovementEnabled
        /// </summary>
        public static bool IsMovementDisabled
        {
            get
            {
                if (IsBotMovementDisabled)
                    return true;

                if (TheFinalCallSettings.Instance.AllowMovement == AllowMovementType.Auto)
                    return IsManualMovementBotActive;

                return TheFinalCallSettings.Instance.AllowMovement != AllowMovementType.All;
            }
        }

        /// <summary>
        ///     True: Bot movement should be disabled by TheFinalCall.  This is controlled
        ///     only by the state of the Hotkeys toggle for movement since we only want
        ///     to interfere with bot movement when the user tells us to
        /// </summary>
        private static bool IsBotMovementDisabled
        {
            get { return !HotkeyManager.IsMovementEnabled; }
        }


        /// <summary>
        ///     True: TheFinalCall Class specific movement is currently disabled.  This could be due to a setting,
        ///     the current Bot, or a Hotkey toggled.  This should be used by all class specific spells
        ///     such as Charge, Roll, Shadow Step, Wild Charge
        /// </summary>
        public static bool IsClassMovementAllowed
        {
            get
            {
                if (IsBotMovementDisabled)
                    return false;

                if (TheFinalCallSettings.Instance.AllowMovement == AllowMovementType.Auto)
                    return !IsManualMovementBotActive;

                return TheFinalCallSettings.Instance.AllowMovement >= AllowMovementType.ClassSpecificOnly;
            }
        }

        /// <summary>
        ///     True: a bot requiring user interaction is running (LazyRaider, RaidBot, etc.)
        /// </summary>
        /// <remarks>
        ///     query the active bot only on a bot event and then cache the result.  we don't
        ///     need to check more often than that
        /// </remarks>
        public static bool IsManualMovementBotActive { get; private set; }
    }
}